package com.ahcloud.edas.gateway.core.application.manager;

import com.ahcloud.edas.common.domain.common.PageResult;
import com.ahcloud.edas.common.enums.DeletedEnum;
import com.ahcloud.edas.common.exception.BizException;
import com.ahcloud.edas.common.util.CollectionUtils;
import com.ahcloud.edas.gateway.core.application.helper.GatewayPluginHelper;
import com.ahcloud.edas.gateway.core.application.service.GatewayPluginService;
import com.ahcloud.edas.gateway.core.domain.plugin.form.PluginAddForm;
import com.ahcloud.edas.gateway.core.domain.plugin.form.PluginUpdateForm;
import com.ahcloud.edas.gateway.core.domain.plugin.query.PluginQuery;
import com.ahcloud.edas.gateway.core.domain.plugin.vo.PluginDetailVO;
import com.ahcloud.edas.gateway.core.domain.plugin.vo.PluginVO;
import com.ahcloud.edas.gateway.core.infrastructure.constant.EnvConstants;
import com.ahcloud.edas.gateway.core.infrastructure.constant.enums.GatewayRetCodeEnum;
import com.ahcloud.edas.gateway.core.infrastructure.constant.enums.PluginStatusEnum;
import com.ahcloud.edas.gateway.core.infrastructure.gateway.enums.ConfigGroupEnum;
import com.ahcloud.edas.gateway.core.infrastructure.gateway.enums.DataEventTypeEnum;
import com.ahcloud.edas.gateway.core.infrastructure.gateway.listener.event.DataChangedEvent;
import com.ahcloud.edas.gateway.core.infrastructure.repository.bean.GatewayPlugin;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

/**
 * @program: edas
 * @description:
 * @author: YuKai Fan
 * @create: 2024/6/13 09:51
 **/
@Slf4j
@Component
public class GatewayPluginManager {
    @Resource
    private GatewayPluginService gatewayPluginService;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 新增Plugin插件
     * @param form
     */
    @Transactional(rollbackFor = Exception.class)
    public void createPlugin(PluginAddForm form) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getName, form.getName())
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.nonNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_EXITED);
        }
        // 判断应用实例是否健康
        GatewayPlugin gatewayPlugin = GatewayPluginHelper.convert(form);
        boolean result = gatewayPluginService.save(gatewayPlugin);
        if (!result) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_ADD_FAILED);
        }
    }

    /**
     * 更新Plugin插件
     * @param form
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePlugin(PluginUpdateForm form) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, form.getId())
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_NOT_EXITED);
        }
        GatewayPlugin gatewayPlugin = GatewayPluginHelper.convert(form);
        gatewayPlugin.setVersion(existedGatewayPlugin.getVersion());
        boolean result = gatewayPluginService.update(
                gatewayPlugin,
                new UpdateWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, form.getId())
                        .eq(GatewayPlugin::getVersion, existedGatewayPlugin.getVersion())
        );
        if (!result) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_UPDATE_FAILED);
        }
        List<String> envList = getOperabilityEnvList(existedGatewayPlugin);
        if (CollectionUtils.isNotEmpty(envList)) {
            applicationEventPublisher.publishEvent(
                    new DataChangedEvent(
                            ImmutablePair.of(gatewayPlugin.getName(), gatewayPlugin.getVersion()),
                            envList,
                            DataEventTypeEnum.UPDATE,
                            ConfigGroupEnum.PLUGIN
                    )
            );
        }
    }

    /**
     * 删除Plugin插件
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)
    public void deletePlugin(Long id) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_NOT_EXITED);
        }
        GatewayPlugin gatewayPlugin = new GatewayPlugin();
        gatewayPlugin.setDeleted(id);
        gatewayPlugin.setVersion(existedGatewayPlugin.getVersion());
        boolean result = gatewayPluginService.update(
                gatewayPlugin,
                new UpdateWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getVersion, existedGatewayPlugin.getVersion())
        );
        if (!result) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_DELETE_FAILED);
        }
        List<String> envList = getOperabilityEnvList(existedGatewayPlugin);
        if (CollectionUtils.isNotEmpty(envList)) {
            applicationEventPublisher.publishEvent(
                    new DataChangedEvent(
                            ImmutablePair.of(existedGatewayPlugin.getName(), gatewayPlugin.getVersion()),
                            EnvConstants.ALL_ENV_LIST,
                            DataEventTypeEnum.DELETE,
                            ConfigGroupEnum.PLUGIN
                    )
            );
        }
    }

    public void changePluginSort(Long id, Integer sort) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_NOT_EXITED);
        }
        GatewayPlugin updateGatewayPlugin = new GatewayPlugin();
        updateGatewayPlugin.setSort(sort);
        updateGatewayPlugin.setVersion(existedGatewayPlugin.getVersion());
        boolean updateResult = gatewayPluginService.update(
                updateGatewayPlugin,
                new UpdateWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getVersion, existedGatewayPlugin.getVersion())
        );
        if (!updateResult) {
            throw new BizException(GatewayRetCodeEnum.VERSION_ERROR);
        }
        List<String> envList = getOperabilityEnvList(existedGatewayPlugin);
        if (CollectionUtils.isNotEmpty(envList)) {
            applicationEventPublisher.publishEvent(
                    new DataChangedEvent(
                            ImmutablePair.of(existedGatewayPlugin.getName(), updateGatewayPlugin.getVersion()),
                            EnvConstants.ALL_ENV_LIST,
                            DataEventTypeEnum.UPDATE,
                            ConfigGroupEnum.PLUGIN
                    )
            );
        }
    }

    /**
     * 根据id查询Plugin信息
     *
     * @param id
     * @return
     */
    public PluginDetailVO findPluginById(Long id) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_NOT_EXITED);
        }
        return GatewayPluginHelper.convertToVo(existedGatewayPlugin);
    }

    /**
     * 分页查询Plugin插件
     * @param query
     * @return
     */
    public PageResult<PluginVO> pagePluginList(PluginQuery query) {
        PageInfo<GatewayPlugin> pageInfo = PageHelper.startPage(query.getPageNo(), query.getPageSize())
                .doSelectPageInfo(
                        () -> gatewayPluginService.list(
                                new QueryWrapper<GatewayPlugin>().lambda()
                                        .orderByDesc(GatewayPlugin::getModifiedTime)
                                        .eq(
                                                StringUtils.isNotBlank(query.getName()),
                                                GatewayPlugin::getName,
                                                query.getName()
                                        )
                                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
                        )
                );
        return GatewayPluginHelper.convertToPageResult(pageInfo);
    }

    /**
     * 刷新插件
     *
     * 加锁操作，防止并发多次刷新
     */
    public void refreshPlugin() {
        applicationEventPublisher.publishEvent(
                new DataChangedEvent(
                        DataEventTypeEnum.REFRESH.name(),
                        EnvConstants.ALL_ENV_LIST,
                        DataEventTypeEnum.REFRESH,
                        ConfigGroupEnum.PLUGIN
                )
        );
    }

    /**
     * 下线插件
     * @param id
     */
    public void offlinePlugin(Long id, String env) {
        operatePlugin(id, PluginStatusEnum.OFFLINE, env);
    }

    /**
     * 下线插件
     * @param id
     */
    public void onlinePlugin(Long id, String env) {
        operatePlugin(id, PluginStatusEnum.ONLINE, env);
    }

    private void operatePlugin(Long id, PluginStatusEnum PluginStatusEnum, String env) {
        GatewayPlugin existedGatewayPlugin = gatewayPluginService.getOne(
                new QueryWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getDeleted, DeletedEnum.NO.value)
        );
        if (Objects.isNull(existedGatewayPlugin)) {
            throw new BizException(GatewayRetCodeEnum.GATEWAY_PLUGIN_NOT_EXITED);
        }
        GatewayPlugin updateGatewayPlugin = new GatewayPlugin();
        Integer status = PluginStatusEnum.getStatus();
        setEnvStatus(env, updateGatewayPlugin, status);
        updateGatewayPlugin.setVersion(existedGatewayPlugin.getVersion());
        boolean updateResult = gatewayPluginService.update(
                updateGatewayPlugin,
                new UpdateWrapper<GatewayPlugin>().lambda()
                        .eq(GatewayPlugin::getId, id)
                        .eq(GatewayPlugin::getVersion, existedGatewayPlugin.getVersion())
        );
        if (!updateResult) {
            throw new BizException(GatewayRetCodeEnum.VERSION_ERROR);
        }
        applicationEventPublisher.publishEvent(
                new DataChangedEvent(
                        ImmutablePair.of(existedGatewayPlugin.getName(), updateGatewayPlugin.getVersion()),
                        Lists.newArrayList(env),
                        DataEventTypeEnum.UPDATE,
                        ConfigGroupEnum.PLUGIN
                )
        );
    }

    private void setEnvStatus(String env, GatewayPlugin updateGatewayPlugin, Integer status) {
        switch (env) {
            case EnvConstants.DEV:
                updateGatewayPlugin.setDev(status);
                break;
            case EnvConstants.TEST:
                updateGatewayPlugin.setTest(status);
                break;
            case EnvConstants.SIT:
                updateGatewayPlugin.setSit(status);
                break;
            case EnvConstants.PRE:
                updateGatewayPlugin.setPre(status);
                break;
            case EnvConstants.PROD:
                updateGatewayPlugin.setProd(status);
                break;
        }
    }

    private List<String> getOperabilityEnvList(GatewayPlugin gatewayPlugin) {
        List<String> envList = Lists.newArrayList();
        if (PluginStatusEnum.enabled(gatewayPlugin.getDev())) {
            envList.add(EnvConstants.DEV);
        }
        if (PluginStatusEnum.enabled(gatewayPlugin.getTest())) {
            envList.add(EnvConstants.TEST);
        }
        if (PluginStatusEnum.enabled(gatewayPlugin.getSit())) {
            envList.add(EnvConstants.SIT);
        }
        if (PluginStatusEnum.enabled(gatewayPlugin.getPre())) {
            envList.add(EnvConstants.PRE);
        }
        if (PluginStatusEnum.enabled(gatewayPlugin.getProd())) {
            envList.add(EnvConstants.PROD);
        }
        return envList;
    }
}
